﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows.Controls;
using System.Windows.Media;


namespace System.Windows.Controls
{
    
    [DefaultEvent("ValueInRangeChanged"),
    Description("Displays a value on an analog gauge. Raises an event if the value enters one of the definable ranges.")]
    public partial class AnalogueGauge : UserControl
    {
        #region enum, var, delegate, event
        /// <summary>
        /// 
        /// </summary>
        public enum NeedleColorEnum
        {
            /// <summary>
            /// 
            /// </summary>
            Gray = 0,
            /// <summary>
            /// 
            /// </summary>
            Red = 1,
            /// <summary>
            /// 
            /// </summary>
            Green = 2,
            /// <summary>
            /// 
            /// </summary>
            Blue = 3,
            /// <summary>
            /// 
            /// </summary>
            Yellow = 4,
            /// <summary>
            /// 
            /// </summary>
            Violet = 5,
            /// <summary>
            /// 
            /// </summary>
            Magenta = 6
        };

        
       
        

        private Needle _needle = new Needle();
        private ObservableCollection<Range> _ranges = new ObservableCollection<Range>();
        private const Byte ZERO = 0;
        private const Byte NUMOFCAPS = 5;
        private const Byte NUMOFRANGES = 5;

        private Single fontBoundY1;
        private Single fontBoundY2;
        private Boolean drawGaugeBackground = true;

        private Single m_value;
        private Boolean[] m_valueIsInRange = { false, false, false, false, false };
        private Byte m_CapIdx = 1;
        private Color[] m_CapColor = { Colors.Black, Colors.Black, Colors.Black, Colors.Black, Colors.Black };
        private String[] m_CapText = { "", "", "", "", "" };
        private Drawing.Point[] m_CapPosition = { new Drawing.Point(10, 10), new Drawing.Point(10, 10), new Drawing.Point(10, 10), new Drawing.Point(10, 10), new Drawing.Point(10, 10) };
        private System.Windows.Point m_Center = new System.Windows.Point(100, 100);
        private Single m_MinValue = -100;
        private Single m_MaxValue = 400;

        private System.Windows.Media.Color m_BaseArcColor = System.Windows.Media.Colors.Gray;
        private Int32 m_BaseArcRadius = 80;
        private Int32 m_BaseArcStart = 135;
        private Int32 m_BaseArcSweep = 270;
        private Int32 m_BaseArcWidth = 2;

        private Color m_ScaleLinesInterColor = Colors.Black;
        private Int32 m_ScaleLinesInterInnerRadius = 73;
        private Int32 m_ScaleLinesInterOuterRadius = 80;
        private Int32 m_ScaleLinesInterWidth = 1;

        private Int32 m_ScaleLinesMinorNumOf = 9;
        private Color m_ScaleLinesMinorColor = Colors.Gray;
        private Int32 m_ScaleLinesMinorInnerRadius = 75;
        private Int32 m_ScaleLinesMinorOuterRadius = 80;
        private Int32 m_ScaleLinesMinorWidth = 1;

        private Single m_ScaleLinesMajorStepValue = 50.0f;
        private Color m_ScaleLinesMajorColor = Colors.Black;
        private Int32 m_ScaleLinesMajorInnerRadius = 70;
        private Int32 m_ScaleLinesMajorOuterRadius = 80;
        private Int32 m_ScaleLinesMajorWidth = 2;

        private Byte m_RangeIdx;
        private Boolean[] m_RangeEnabled = { true, true, false, false, false };
        private Color[] m_RangeColor = { Colors.LightGreen, Colors.Red, SystemColors.ControlColor, SystemColors.ControlColor, SystemColors.ControlColor };
        private Single[] m_RangeStartValue = { -100.0f, 300.0f, 0.0f, 0.0f, 0.0f };
        private Single[] m_RangeEndValue = { 300.0f, 400.0f, 0.0f, 0.0f, 0.0f };
        private Int32[] m_RangeInnerRadius = { 70, 70, 70, 70, 70 };
        private Int32[] m_RangeOuterRadius = { 80, 80, 80, 80, 80 };

        private Int32 m_ScaleNumbersRadius = 95;
        private Color m_ScaleNumbersColor = Colors.Black;
        private String m_ScaleNumbersFormat;
        private Int32 m_ScaleNumbersStartScaleLine;
        private Int32 m_ScaleNumbersStepScaleLines = 1;
        private Int32 m_ScaleNumbersRotation = 0;

        private Int32 m_NeedleType = 0;
        private Int32 m_NeedleRadius = 80;
        private Color m_NeedleColor1 = Colors.Red;
        private Color m_NeedleColor2 = Colors.DimGray;
        private Int32 m_NeedleWidth = 2;

        /// <summary>
        /// 
        /// </summary>
        public class ValueInRangeChangedEventArgs : EventArgs
        {
            /// <summary>
            /// 
            /// </summary>
            public Int32 valueInRange;
            /// <summary>
            /// 
            /// </summary>
            /// <param name="valueInRange"></param>
            public ValueInRangeChangedEventArgs(Int32 valueInRange)
            {
                this.valueInRange = valueInRange;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public delegate void ValueInRangeChangedDelegate(Object sender, ValueInRangeChangedEventArgs e);
        /// <summary>
        /// 
        /// </summary>
        [Description("This event is raised if the value falls into a defined range.")]
        public event ValueInRangeChangedDelegate ValueInRangeChanged;
#endregion

#region hidden , overridden inherited properties
        /// <summary>
        /// 
        /// </summary>
        public new Boolean AllowDrop
        {
            get
            {
                return false;
            }
            set
            {

            }
        }
        /// <summary>
        /// 
        /// </summary>
        public new Boolean AutoSize
        {
            get
            {
                return false;
            }
            set
            {

            }
        }
        /// <summary>
        /// 
        /// </summary>
        public new Boolean ForeColor
        {
            get
            {
                return false;
            }
            set
            {
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public new Boolean ImeMode
        {
            get
            {
                return false;
            }
            set
            {
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public new System.Windows.Media.Brush Background
        {
            get
            {
                
                return base.Background;
            }
            set
            {
                base.Background = value;
                drawGaugeBackground = true;
                InvalidateVisual();
            }
        }

        public Needle Needle
        {
            get
            {
                return _needle;
            }
            set
            {
                if (_needle != value)
                {
                    _needle.PropertyChanged -= _needle_PropertyChanged;
                    _needle = value;
                    _needle.PropertyChanged += _needle_PropertyChanged;
                }

            }
        }
        
        #endregion
        /// <summary>
        /// 
        /// </summary>
        public AnalogueGauge()
        {
            InitializeComponent();
            _ranges.CollectionChanged += _ranges_CollectionChanged;
            _needle.PropertyChanged += _needle_PropertyChanged;
          
        }

        void _needle_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            drawGaugeBackground = true;
            InvalidateVisual();
        }

        void _ranges_CollectionChanged(object sender, Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if(e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach(Range item in e.NewItems)
                {
                    item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
                }
                
            }
            if(e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (Range item in e.OldItems)
                {
                    item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
                }
            }
            drawGaugeBackground = true;
            InvalidateVisual();
        }

        void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            drawGaugeBackground = true;
            InvalidateVisual();
        }

#region properties
        
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The value.")]
        public Single Value
        {
            get
            {
                return m_value;
            }
            set
            {
                if (m_value != value)
                {
                    m_value = Math.Min(Math.Max(value, m_MinValue), m_MaxValue);

                    if (DesignerProperties.GetIsInDesignMode(this))
                    {
                        drawGaugeBackground = true;
                    }

                    for (Int32 counter = 0; counter < NUMOFRANGES - 1; counter++)
                    {
                        if ((m_RangeStartValue[counter] <= m_value)
                        && (m_value <= m_RangeEndValue[counter])
                        && (m_RangeEnabled[counter]))
                        {
                            if (!m_valueIsInRange[counter])
                            {
                                if (ValueInRangeChanged!=null)
                                {
                                    ValueInRangeChanged(this, new ValueInRangeChangedEventArgs(counter));
                                }
                            }
                        }
                        else
                        {
                            m_valueIsInRange[counter] = false;
                        }
                    }                   
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.RefreshProperties(RefreshProperties.All),
        System.ComponentModel.Description("The caption index. set this to a value of 0 up to 4 to change the corresponding caption's properties.")]
        public Byte Cap_Idx
        {
            get
            {
                return m_CapIdx;
            }
            set
            {
                if ((m_CapIdx != value)
                && (0 <= value)
                && (value < 5))
                {
                    m_CapIdx = value;
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The color of the caption text.")]
        private Color CapColor
        {
            get
            {
                return m_CapColor[m_CapIdx];
            }
            set
            {
                if (m_CapColor[m_CapIdx] != value)
                {
                    m_CapColor[m_CapIdx] = value;
                    CapColors = m_CapColor;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public Color[] CapColors
        {
            get
            {
                return m_CapColor;
            }
            set
            {
                m_CapColor = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The text of the caption.")]
        public String CapText
        {
            get
            {
                return m_CapText[m_CapIdx];
            }
            set
            {
                if (m_CapText[m_CapIdx] != value)
                {
                    m_CapText[m_CapIdx] = value;
                    CapsText = m_CapText;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public String[] CapsText
        {
            get
            {
                return m_CapText;
            }
            set
            {
                for (Int32 counter = 0; counter < 5; counter++)
                {
                    m_CapText[counter] = value[counter];
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The position of the caption.")]
        public Drawing.Point CapPosition
        {
            get
            {
                return m_CapPosition[m_CapIdx];
            }
            set
            {
                if (m_CapPosition[m_CapIdx] != value)
                {
                    m_CapPosition[m_CapIdx] = value;
                    CapsPosition = m_CapPosition;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public Drawing.Point[] CapsPosition
        {
            get
            {
                return m_CapPosition;
            }
            set
            {
                m_CapPosition = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The center of the gauge (in the control's client area).")]
        public System.Windows.Point Center
        {
            get
            {
                return m_Center;
            }
            set
            {
                if (m_Center != value)
                {
                    m_Center = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The minimum value to show on the scale.")]
        public Single MinValue
        {
            get
            {
                return m_MinValue;
            }
            set
            {
                if ((m_MinValue != value)
                && (value < m_MaxValue))
                {
                    m_MinValue = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The maximum value to show on the scale.")]
        public Single MaxValue
        {
            get
            {
                return m_MaxValue;
            }
            set
            {
                if ((m_MaxValue != value)
                && (value > m_MinValue))
                {
                    m_MaxValue = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The color of the base arc.")]
        public System.Windows.Media.Color BaseArcColor
        {
            get
            {
                return m_BaseArcColor;
            }
            set
            {
                if (m_BaseArcColor != value)
                {
                    m_BaseArcColor = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The radius of the base arc.")]
        public Int32 BaseArcRadius
        {
            get
            {
                return m_BaseArcRadius;
            }
            set
            {
                if (m_BaseArcRadius != value)
                {
                    m_BaseArcRadius = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The start angle of the base arc.")]
        public Int32 BaseArcStart
        {
            get
            {
                return m_BaseArcStart;
            }
            set
            {
                if (m_BaseArcStart != value)
                {
                    m_BaseArcStart = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The sweep angle of the base arc.")]
        public Int32 BaseArcSweep
        {
            get
            {
                return m_BaseArcSweep;
            }
            set
            {
                if (m_BaseArcSweep != value)
                {
                    m_BaseArcSweep = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The width of the base arc.")]
        public Int32 BaseArcWidth
        {
            get
            {
                return m_BaseArcWidth;
            }
            set
            {
                if (m_BaseArcWidth != value)
                {
                    m_BaseArcWidth = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The color of the inter scale lines which are the middle scale lines for an uneven number of minor scale lines.")]
        public Color ScaleLinesInterColor
        {
            get
            {
                return m_ScaleLinesInterColor;
            }
            set
            {
                if (m_ScaleLinesInterColor != value)
                {
                    m_ScaleLinesInterColor = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The inner radius of the inter scale lines which are the middle scale lines for an uneven number of minor scale lines.")]
        public Int32 ScaleLinesInterInnerRadius
        {
            get
            {
                return m_ScaleLinesInterInnerRadius;
            }
            set
            {
                if (m_ScaleLinesInterInnerRadius != value)
                {
                    m_ScaleLinesInterInnerRadius = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The outer radius of the inter scale lines which are the middle scale lines for an uneven number of minor scale lines.")]
        public Int32 ScaleLinesInterOuterRadius
        {
            get
            {
                return m_ScaleLinesInterOuterRadius;
            }
            set
            {
                if (m_ScaleLinesInterOuterRadius != value)
                {
                    m_ScaleLinesInterOuterRadius = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The width of the inter scale lines which are the middle scale lines for an uneven number of minor scale lines.")]
        public Int32 ScaleLinesInterWidth
        {
            get
            {
                return m_ScaleLinesInterWidth;
            }
            set
            {
                if (m_ScaleLinesInterWidth != value)
                {
                    m_ScaleLinesInterWidth = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The number of minor scale lines.")]
        public Int32 ScaleLinesMinorNumOf
        {
            get
            {
                return m_ScaleLinesMinorNumOf;
            }
            set
            {
                if (m_ScaleLinesMinorNumOf != value)
                {
                    m_ScaleLinesMinorNumOf = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The color of the minor scale lines.")]
        public Color ScaleLinesMinorColor
        {
            get
            {
                return m_ScaleLinesMinorColor;
            }
            set
            {
                if (m_ScaleLinesMinorColor != value)
                {
                    m_ScaleLinesMinorColor = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The inner radius of the minor scale lines.")]
        public Int32 ScaleLinesMinorInnerRadius
        {
            get
            {
                return m_ScaleLinesMinorInnerRadius;
            }
            set
            {
                if (m_ScaleLinesMinorInnerRadius != value)
                {
                    m_ScaleLinesMinorInnerRadius = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The outer radius of the minor scale lines.")]
        public Int32 ScaleLinesMinorOuterRadius
        {
            get
            {
                return m_ScaleLinesMinorOuterRadius;
            }
            set
            {
                if (m_ScaleLinesMinorOuterRadius != value)
                {
                    m_ScaleLinesMinorOuterRadius = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The width of the minor scale lines.")]
        public Int32 ScaleLinesMinorWidth
        {
            get
            {
                return m_ScaleLinesMinorWidth;
            }
            set
            {
                if (m_ScaleLinesMinorWidth != value)
                {
                    m_ScaleLinesMinorWidth = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The step value of the major scale lines.")]
        public Single ScaleLinesMajorStepValue
        {
            get
            {
                return m_ScaleLinesMajorStepValue;
            }
            set
            {
                if ((m_ScaleLinesMajorStepValue != value) && (value > 0))
                {
                    m_ScaleLinesMajorStepValue = Math.Max(Math.Min(value, m_MaxValue), m_MinValue);
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The color of the major scale lines.")]
        public Color ScaleLinesMajorColor
        {
            get
            {
                return m_ScaleLinesMajorColor;
            }
            set
            {
                if (m_ScaleLinesMajorColor != value)
                {
                    m_ScaleLinesMajorColor = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The inner radius of the major scale lines.")]
        public Int32 ScaleLinesMajorInnerRadius
        {
            get
            {
                return m_ScaleLinesMajorInnerRadius;
            }
            set
            {
                if (m_ScaleLinesMajorInnerRadius != value)
                {
                    m_ScaleLinesMajorInnerRadius = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The outer radius of the major scale lines.")]
        public Int32 ScaleLinesMajorOuterRadius
        {
            get
            {
                return m_ScaleLinesMajorOuterRadius;
            }
            set
            {
                if (m_ScaleLinesMajorOuterRadius != value)
                {
                    m_ScaleLinesMajorOuterRadius = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The width of the major scale lines.")]
        public Int32 ScaleLinesMajorWidth
        {
            get
            {
                return m_ScaleLinesMajorWidth;
            }
            set
            {
                if (m_ScaleLinesMajorWidth != value)
                {
                    m_ScaleLinesMajorWidth = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.RefreshProperties(RefreshProperties.All),
        System.ComponentModel.Description("The range index. set this to a value of 0 up to 4 to change the corresponding range's properties.")]
        public Byte Range_Idx
        {
            get
            {
                return m_RangeIdx;
            }
            set
            {
                if ((m_RangeIdx != value)
                && (0 <= value)
                && (value < NUMOFRANGES))
                {
                    m_RangeIdx = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("Enables or disables the range selected by Range_Idx.")]
        public Boolean RangeEnabled
        {
            get
            {
                return m_RangeEnabled[m_RangeIdx];
            }
            set
            {
                if (m_RangeEnabled[m_RangeIdx] != value)
                {
                    m_RangeEnabled[m_RangeIdx] = value;
                    RangesEnabled = m_RangeEnabled;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public Boolean[] RangesEnabled
        {
            get
            {
                return m_RangeEnabled;
            }
            set
            {
                m_RangeEnabled = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The color of the range.")]
        public Color RangeColor
        {
            get
            {
                return m_RangeColor[m_RangeIdx];
            }
            set
            {
                if (m_RangeColor[m_RangeIdx] != value)
                {
                    m_RangeColor[m_RangeIdx] = value;
                    RangesColor = m_RangeColor;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public Color[] RangesColor
        {
            get
            {
                return m_RangeColor;
            }
            set
            {
                m_RangeColor = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The start value of the range, must be less than RangeEndValue.")]
        public Single RangeStartValue
        {
            get
            {
                return m_RangeStartValue[m_RangeIdx];
            }
            set
            {
                if ((m_RangeStartValue[m_RangeIdx] != value)
                && (value < m_RangeEndValue[m_RangeIdx]))
                {
                    m_RangeStartValue[m_RangeIdx] = value;
                    RangesStartValue = m_RangeStartValue;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public Single[] RangesStartValue
        {
            get
            {
                return m_RangeStartValue;
            }
            set
            {
                m_RangeStartValue = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The end value of the range. Must be greater than RangeStartValue.")]
        public Single RangeEndValue
        {
            get
            {
                return m_RangeEndValue[m_RangeIdx];
            }
            set
            {
                if ((m_RangeEndValue[m_RangeIdx] != value)
                && (m_RangeStartValue[m_RangeIdx] < value))
                {
                    m_RangeEndValue[m_RangeIdx] = value;
                    RangesEndValue = m_RangeEndValue;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public Single[] RangesEndValue
        {
            get
            {
                return m_RangeEndValue;
            }
            set
            {
                m_RangeEndValue = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The inner radius of the range.")]
        public Int32 RangeInnerRadius
        {
            get
            {
                return m_RangeInnerRadius[m_RangeIdx];
            }
            set
            {
                if (m_RangeInnerRadius[m_RangeIdx] != value)
                {
                    m_RangeInnerRadius[m_RangeIdx] = value;
                    RangesInnerRadius = m_RangeInnerRadius;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public Int32[] RangesInnerRadius
        {
            get
            {
                return m_RangeInnerRadius;
            }
            set
            {
                m_RangeInnerRadius = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The inner radius of the range.")]
        public Int32 RangeOuterRadius
        {
            get
            {
                return m_RangeOuterRadius[m_RangeIdx];
            }
            set
            {
                if (m_RangeOuterRadius[m_RangeIdx] != value)
                {
                    m_RangeOuterRadius[m_RangeIdx] = value;
                    RangesOuterRadius = m_RangeOuterRadius;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(false)]
        public Int32[] RangesOuterRadius
        {
            get
            {
                return m_RangeOuterRadius;
            }
            set
            {
                m_RangeOuterRadius = value;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The radius of the scale numbers.")]
        public Int32 ScaleNumbersRadius
        {
            get
            {
                return m_ScaleNumbersRadius;
            }
            set
            {
                if (m_ScaleNumbersRadius != value)
                {
                    m_ScaleNumbersRadius = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The color of the scale numbers.")]
        public Color ScaleNumbersColor
        {
            get
            {
                return m_ScaleNumbersColor;
            }
            set
            {
                if (m_ScaleNumbersColor != value)
                {
                    m_ScaleNumbersColor = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The format of the scale numbers.")]
        public String ScaleNumbersFormat
        {
            get
            {
                return m_ScaleNumbersFormat;
            }
            set
            {
                if (m_ScaleNumbersFormat != value)
                {
                    m_ScaleNumbersFormat = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The number of the scale line to start writing numbers next to.")]
        public Int32 ScaleNumbersStartScaleLine
        {
            get
            {
                return m_ScaleNumbersStartScaleLine;
            }
            set
            {
                if (m_ScaleNumbersStartScaleLine != value)
                {
                    m_ScaleNumbersStartScaleLine = Math.Max(value, 1);
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The number of scale line steps for writing numbers.")]
        public Int32 ScaleNumbersStepScaleLines
        {
            get
            {
                return m_ScaleNumbersStepScaleLines;
            }
            set
            {
                if (m_ScaleNumbersStepScaleLines != value)
                {
                    m_ScaleNumbersStepScaleLines = Math.Max(value, 1);
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The angle relative to the tangent of the base arc at a scale line that is used to rotate numbers. set to 0 for no rotation or e.g. set to 90.")]
        public Int32 ScaleNumbersRotation
        {
            get
            {
                return m_ScaleNumbersRotation;
            }
            set
            {
                if (m_ScaleNumbersRotation != value)
                {
                    m_ScaleNumbersRotation = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The type of the needle, currently only type 0 and 1 are supported. Type 0 looks nicers but if you experience performance problems you might consider using type 1.")]
        public Int32 NeedleType
        {
            get
            {
                return m_NeedleType;
            }
            set
            {
                if (m_NeedleType != value)
                {
                    m_NeedleType = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The radius of the needle.")]
        public Int32 NeedleRadius
        {
            get
            {
                return m_NeedleRadius;
            }
            set
            {
                if (m_NeedleRadius != value)
                {
                    m_NeedleRadius = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The first color of the needle.")]
        public Color NeedleColor1
        {
            get
            {
                return m_NeedleColor1;
            }
            set
            {
                if (m_NeedleColor1 != value)
                {
                    m_NeedleColor1 = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The second color of the needle.")]
        public Color NeedleColor2
        {
            get
            {
                return m_NeedleColor2;
            }
            set
            {
                if (m_NeedleColor2 != value)
                {
                    m_NeedleColor2 = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }
        /// <summary>
        /// 
        /// </summary>
        [System.ComponentModel.Browsable(true),
        System.ComponentModel.Category("AGauge"),
        System.ComponentModel.Description("The width of the needle.")]
        public Int32 NeedleWidth
        {
            get
            {
                return m_NeedleWidth;
            }
            set
            {
                if (m_NeedleWidth != value)
                {
                    m_NeedleWidth = value;
                    drawGaugeBackground = true;
                    InvalidateVisual();
                }
            }
        }

        public ObservableCollection<Range> Ranges
        {
            get { return _ranges; }
            set { _ranges = value; }
        }

        #endregion



#region base member overrides

        private static void DrawLine(DrawingContext graphics, System.Windows.Point pointOne, System.Windows.Point pointTwo,
                              Media.Color color, int lineThickness)
        {
            var pen = new System.Windows.Media.Pen(new System.Windows.Media.SolidColorBrush(color), lineThickness);
            {
                pen.StartLineCap = PenLineCap.Round;
                pen.EndLineCap = PenLineCap.Round;

                graphics.DrawLine(pen, pointOne, pointTwo);
            }

        }

        private System.Windows.Point GetCoordinate(System.Windows.Point center, int radius, double angle)
        {
            double radians = Math.PI * angle / 180;

            return new System.Windows.Point(center.X + radius * (float)Math.Cos(radians),
                              center.Y - radius * (float)Math.Sin(radians));
        }

        
        
        static void DrawArc(DrawingContext drawingContext, Media.Brush brush,Media.Pen pen, Point start, Point end,bool large, Size radius)
        {
            // setup the geometry object
            PathGeometry geometry = new PathGeometry();
            PathFigure figure = new PathFigure();
            geometry.Figures.Add(figure);
            figure.StartPoint = start;

            // add the arc to the geometry
            figure.Segments.Add(new ArcSegment(end, radius,0,large, SweepDirection.Clockwise, true));

            // draw the arc
            drawingContext.DrawGeometry(brush, pen, geometry);
        }

        private static void DrawCircle(DrawingContext drawingContext,Point center, double radius, Brush color)
        {
            drawingContext.DrawEllipse(color, new Media.Pen(new Media.SolidColorBrush(Colors.DarkGray), 2), center, radius, radius);
        }

        private void DrawTicks(DrawingContext drawingContext,float major,int ticks, Point center, int innerRadius, int outerRadius, Color color, int width)
        {
            float angleStepSize = m_BaseArcSweep / major;
            for (int i = 0; i < major; i++)
            {
                float majorAngle = m_BaseArcStart - (angleStepSize * i);
                float tickStep = angleStepSize / ticks;
                for(int tick = 0 ; tick < ticks;tick++)
                {
                    float tickAngle = majorAngle - (tickStep * tick);
                    Point start = new Point
                    {
                        X = center.X + innerRadius * Math.Cos((Math.PI / 180) * (tickAngle)),
                        Y = center.Y - innerRadius * Math.Sin((Math.PI / 180) * (tickAngle))
                    };
                    Point stop = new Point
                    {
                        X = center.X + outerRadius * Math.Cos((Math.PI / 180) * (tickAngle)),
                        Y = center.Y - outerRadius * Math.Sin((Math.PI / 180) * (tickAngle))
                    };
                    DrawLine(drawingContext, start, stop, color, width);
                }
                
            }
        }

        private void DrawScale(DrawingContext drawingContext,float number,Point center, int innerRadius, int outerRadius, Color color, int width)
        {
            //int numberOfTicks = (int)((float)(m_MaxValue - m_MinValue)/(float)m_ScaleLinesMajorStepValue);
            float angleStepSize = m_BaseArcSweep/number;
            for(int i = 0; i <= number;i++ )
            {
                float currentAngle = m_BaseArcStart - (angleStepSize*i);
                Point start = new Point
                {
                    X = center.X + innerRadius * Math.Cos((Math.PI / 180) * (currentAngle)),
                    Y = center.Y - innerRadius * Math.Sin((Math.PI / 180) * (currentAngle))
                };
                Point stop = new Point
                {
                    X = center.X + outerRadius * Math.Cos((Math.PI / 180) * (currentAngle)),
                    Y = center.Y - outerRadius * Math.Sin((Math.PI / 180) * (currentAngle))
                };
                DrawLine(drawingContext, start, stop, color, width);
            }
        }

        private static void DrawNeedle(DrawingContext drawingContext,Point center, int needleAngle ,Needle needle) 
        {
            needle.Render(drawingContext,center,needleAngle);
            //Point end = new Point
            //{
            //    X = center.X + needle.Radius * Math.Cos((Math.PI / 180) * (needleAngle)),
            //    Y = center.Y - needle.Radius * Math.Sin((Math.PI / 180) * (needleAngle))
            //};
            //DrawLine(drawingContext, new Point(center.X, center.Y), end, needle.Color, needle.Width);
        }

        private void DrawRanges(System.Windows.Media.DrawingContext drawingContext,IEnumerable<Range> ranges )
        {
            foreach(var range in ranges)
            {
                int thickness = range.OuterRadius - range.InnerRadius;

                int radius = range.InnerRadius + (thickness / 2);
                Point start = new Point
                {
                    X = m_Center.X + radius * Math.Cos((Math.PI / 180) * (range.Start)),
                    Y = m_Center.Y - radius * Math.Sin((Math.PI / 180) * (range.Start))
                };

                

                Point stop = new Point
                {
                    X = m_Center.X + radius * Math.Cos((Math.PI * (range.End)) / 180),
                    Y = m_Center.Y - radius * Math.Sin((Math.PI * (range.End)) / 180)
                };

                

                DrawArc(drawingContext, null, new Media.Pen(new Media.SolidColorBrush(range.Color), thickness), start, stop, range.End < 45, new Size(radius, radius));
                
            }
        }

        protected override void OnRender(System.Windows.Media.DrawingContext drawingContext)
        {
        
            if ((Width < 10) || (Height < 10))
            {
                return;
            }
            
            if (drawGaugeBackground)
            {
                drawGaugeBackground = false;
                Point start = new Point
                                  {
                                      X = m_Center.X + m_BaseArcRadius*Math.Cos((Math.PI/180)*(m_BaseArcStart)),
                                      Y = m_Center.Y - m_BaseArcRadius*Math.Sin((Math.PI/180)*(m_BaseArcStart))
                                  };
                int adjustedArcStop = (m_BaseArcStart ) - m_BaseArcSweep;
                
                Point stop = new Point{
                                      X = m_Center.X + m_BaseArcRadius*Math.Cos((Math.PI*(adjustedArcStop))/180),
                                      Y = m_Center.Y - m_BaseArcRadius*Math.Sin((Math.PI*(adjustedArcStop))/180)
                                  };
                DrawArc(drawingContext, null, new Media.Pen(new Media.SolidColorBrush(BaseArcColor), BaseArcWidth), start, stop, adjustedArcStop < 45, new Size(m_BaseArcRadius, m_BaseArcRadius));
                //PathGeometry geometry = new PathGeometry();
                //PathFigure figure = new PathFigure();
                //geometry.Figures.Add(figure);
                //figure.StartPoint = start;

                //// add the arc to the geometry
                //figure.Segments.Add(new ArcSegment(stop, new Size(m_BaseArcRadius, m_BaseArcRadius), 0, adjustedArcStop < 45? true:false, SweepDirection.Clockwise, true));

                //// draw the arc
                //drawingContext.DrawGeometry(null, new Media.Pen(new Media.SolidColorBrush(BaseArcColor), BaseArcWidth), geometry);

                DrawRanges(drawingContext, _ranges);

                DrawCircle(drawingContext, m_Center, this.Needle.BaseRadius, this.Needle.BaseColor);

                DrawScale(drawingContext,
                          ((float) (m_MaxValue - m_MinValue)/(float) m_ScaleLinesMajorStepValue), m_Center,
                          m_ScaleLinesMajorInnerRadius, m_ScaleLinesMajorOuterRadius, m_ScaleLinesMajorColor,
                          m_ScaleLinesMajorWidth);

                DrawTicks(drawingContext,
                          ((float) (m_MaxValue - m_MinValue)/(float) m_ScaleLinesMajorStepValue),
                          m_ScaleLinesMinorNumOf,
                          m_Center,
                          m_ScaleLinesInterInnerRadius,
                          m_ScaleLinesInterOuterRadius,
                          m_ScaleLinesInterColor,
                          m_ScaleLinesInterWidth);

                DrawNeedle(drawingContext,
                    Center,
                    m_BaseArcStart - (int)((float)Value / (float)(MaxValue - MinValue) * (float)m_BaseArcSweep),
                    _needle );

                

            }
        }

        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            drawGaugeBackground = true;
            InvalidateVisual();
            base.OnRenderSizeChanged(sizeInfo);
        }

        
#endregion

    }
}
